package org.budo.template.lexer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;

import org.budo.template.TemplateLexer;

/**
 * @author lmw
 */
public class TemplateLexerImpl implements TemplateLexer {
    private static final String KEY_WORD = "关键字";

    private static final String OPERATOR = "运算符";

    private static final String IDENTIFIER_ERROR = "符号命名错误";

    private static final String DIGIT = "数字";

    private static final String IDENTIFIER = "标识符";

    /* 记录行号 */
    private static int line = 1;

    /* 记录列号 */
    private static int rows = 0;

    /* 下一个读入字符 */
    private char peek = ' ';

    private Hashtable<String, String> words = new Hashtable<String, String>();

    /* erros 表 */
    private Hashtable<String, Error> errors = new Hashtable<String, Error>();

    /* token序列 */
    private List<String> tokens = new LinkedList<String>();

    /* 读取文件变量 */
    private BufferedReader reader = null;

    /* 保存当前是否读取到了文件的结尾 */
    private Boolean isEnd = false;

    /* 是否读取到文件的结尾 */
    public Boolean getReaderState() {
        return this.isEnd;
    }

    /*
     * 构造函数中将关键字和类型添加到hashtable words中
     */
    public TemplateLexerImpl(Reader reader) {
        this.reader = new BufferedReader(reader);

        /* 关键字 */
        this.putWord(Word.AND, OPERATOR);
        this.putWord(Word.OR, OPERATOR);
        this.putWord(Word.EQ_2, OPERATOR);
        this.putWord(Word.NE, OPERATOR);
        this.putWord(Word.EQ, OPERATOR);
        this.putWord(Word.GT, OPERATOR);
        this.putWord(Word.LT, OPERATOR);
        this.putWord(Word.NOT, OPERATOR);
        this.putWord(Word.TRUE, KEY_WORD);
        this.putWord(Word.FALSE, KEY_WORD);
        this.putWord(Word.INT, KEY_WORD);
        this.putWord(Word.DOUBLE, KEY_WORD);
        this.putWord(Word.FLOAT, KEY_WORD);
        this.putWord(Word.CHAR, KEY_WORD);
        this.putWord(Word.BOOLEAN, KEY_WORD);
        this.putWord(Word.STRING, KEY_WORD);
        this.putWord(Word.PRIVATE, KEY_WORD);
        this.putWord(Word.PROTECTED, KEY_WORD);

        this.putWord(Word.DOT, OPERATOR);
    }

    private void putWord(String key, String vaule) {
        words.put(key, vaule);
    }

    /* 读文件字符 */
    private void readch() throws IOException {
        /* 这里应该是使用的是 */
        peek = (char) reader.read();
        if ((int) peek == 0xffff) {
            this.isEnd = true;
        }
    }

    /* 读文件下一个字符 */
    private Boolean readch(char ch) throws IOException {
        this.readch();

        if (this.peek != ch) {
            return false;
        }

        this.peek = ' ';
        return true;
    }

    /* 程序词法分析 */
    public String scan() throws IOException {
        /* 消除空白 */
        for (; true; this.readch()) {
            if (peek == ' ' || peek == '\t') {
                rows++;
                continue;
            } else if (peek == '\n') {
                line = line + 1;
                rows = 0;
            } else
                break;
        }

        rows++;

        /* 下面开始分割关键字，标识符等信息 */
        switch (peek) {
        /* 对于 ==, >=, <=, !=的区分使用状态机实现 */
        case '=':
            if (readch('=')) {
                tokens.add("==");
                return Word.EQ_2;
            } else {
                tokens.add("=");
                return Word.EQ;
            }
        case '>':
            if (readch('=')) {
                tokens.add(">=");
                return Word.GE;
            } else {
                tokens.add(">");
                return Word.GT;
            }
        case '<':
            if (readch('=')) {
                tokens.add("<=");
                return Word.LE;
            } else {
                tokens.add("<");
                return Word.LT;
            }
        case '!':
            if (readch('=')) {
                tokens.add("!=");
                return Word.NE;
            } else {

                tokens.add("!");
                return Word.NOT;
            }
        case '~':
        case '@':
        case '$':
        case '%':
        case '^':
        case '&':
            String tmp = "";
            do {
                tmp += peek;
                this.readch();
                if (peek == ' ' || peek == '\t') {
                    break;
                }
            } while (true);

            errors.put(tmp, new Error(line, rows, IDENTIFIER_ERROR));
            return tmp;
        }

        /*
         * 下面是对数字的识别，根据文法的规定的话，这里的 数字只要是能够识别整数就行.
         */
        if (Character.isDigit(peek)) {
            int value = 0;
            String tmp = "";

            do {
                value = 10 * value + Character.digit(peek, 10);
                tmp += value;
                readch();
            } while (Character.isDigit(peek));

            if ((peek >= 'a' && peek <= 'z') || (peek >= 'A' && peek <= 'Z')) {
                tmp = tmp + peek;
                checkTag(tmp);
                tokens.add(tmp);
                this.readch();
                return tmp;
            } else {
                tokens.add(value + "");
                words.put(value + "", DIGIT);
                return value + "";
            }
        }

        /*
         * 关键字或者是标识符的识别
         */
        if (Character.isLetter(peek)) {
            StringBuffer buf = new StringBuffer();

            /* 首先得到整个的一个分割 */
            do {
                buf.append(peek);
                this.readch();
            } while (Character.isLetterOrDigit(peek));
            /* 判断是关键字还是标识符 */
            String key = buf.toString();
            String word = words.get(key);
            /* t 为关键字 */
            /* 如果是关键字或者是类型的话，w不应该是空的 */
            if (word != null) {
                tokens.add(key);
                return key; /* 说明是关键字 或者是类型名 */
            }

            /* 否则就是一个标识符id */
            if (this.checkTag(key)) {
                tokens.add(key);
                words.put(key, IDENTIFIER);
                return key;
            } else {
                return key;
            }
        }

        /* peek中的任意字符都被认为是词法单元返回 */
        String tmp = "" + (char) peek;
        // table.put(tok, "Token or Seprator");
        if ((int) peek != 0xffff)
            tokens.add(tmp);
        peek = ' ';
        return tmp;
    }

    /* 标识符规则 */
    private boolean checkTag(String str) {
        char beg = str.charAt(0);
        /* 不能以符号开头 */
        switch (beg) {
        /* 不能以数字开头 */
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            errors.put(str, new Error(line, rows, "不能用数字开头"));
            return false;
        case '~':
        case '!':
        case '@':
        case '$':
        case '%':
        case '^':
        case '&':
        case '*':
        case '(':
        case ')':
            errors.put(str, new Error(line, rows, "不能用符号开头"));
            return false;
        default:
            return true;
        }
    }

    public Hashtable<String, Error> getErrors() {
        return this.errors;
    }

    public Hashtable<String, String> getWords() {
        return this.words;
    }

    public List<String> getTokens() {
        return Collections.unmodifiableList(this.tokens);
    }
}
